<?php

namespace EasySwoole\EasySwoole;


use App\Common\JsonSendMessage;
use App\Cron\Queue\TestQueue;
use App\Cron\QueueProcess\TestQueueProcess;
use App\Models\UserModel;
use App\Pool\RabbitmqPool;
use App\Pool\RedisPool;
use App\Process\FastCacheQueue;
use App\Process\HotReload;
use App\Socket\Tcp\TcpParser;
use App\Socket\WebSocket\WebSocketParser;
use EasySwoole\Component\Timer;
use EasySwoole\EasySwoole\Swoole\EventRegister;
use EasySwoole\EasySwoole\AbstractInterface\Event;
use EasySwoole\FastCache\Cache;
use EasySwoole\Http\Message\Status;
use EasySwoole\Http\Request;
use EasySwoole\Http\Response;
use EasySwoole\Mysqli\Client;
use EasySwoole\Mysqli\Config as MysqlConfig;
use EasySwoole\ORM\Db\Connection;
use EasySwoole\ORM\DbManager;
use EasySwoole\Pool\Manager;
use EasySwoole\Queue\Driver\Redis;
use EasySwoole\Queue\Job;
use EasySwoole\Redis\Config\RedisConfig;
use EasySwoole\RedisPool\RedisPool as RedisPoolEasy;
use EasySwoole\Socket\Client\WebSocket;
use EasySwoole\Socket\Dispatcher;

class EasySwooleEvent implements Event
{

    public static function initialize()
    {
        // TODO: Implement initialize() method.
        date_default_timezone_set('Asia/Shanghai');
    }

    public static function mainServerCreate(EventRegister $register)
    {

        self::is_log_dir();//检查日志目录是否存在
        self::serverReload();//监听服务器是否需要重启 热启动
        self::mysql_pool_conn(); //注册mysql连接对象
        self::redis_pool_conn(); //注册redis连接对象
        self::redis_pool(); //注册redis连接对象
        self::registerQueueProcess(); //注册消费队列进程
        self::registerFastCacheQueueServer(); //注册延时队列服务进程
        self::registerQueueProcessConsumeProcess(); //注册延时队列进程
        self::registerWebSocketServer($register);// 注册websocket 服务
        self::registerTcpServer(); //注册tcp服务
        self::rabbitmq_pool_conn();

        // TODO: Implement mainServerCreate() method.
    }

    public static function onRequest(Request $request, Response $response): bool
    {
        self::SetCrossDomain($request, $response);// 设置跨域问题
        self::setRequestLog($request);//设置请求日志
        return true;
    }

    public static function afterRequest(Request $request, Response $response): void
    {
        // TODO: Implement afterAction() method.
    }

    //注册热重启进程监听
    private static function serverReload()
    {
//        因为虚拟机中inotify无法监听到FTP/SFTP等文件上传的事件，将 disableInotify 设置为 true ，可以关闭inotify方式的热重启，使得虚拟机环境下，强制使用文件循环扫描来触发重载操作，同理 OSX 开发环境下，没有Inotify扩展，将自动使用扫描式重载
        $swooleServer = ServerManager::getInstance()->getSwooleServer();
        $swooleServer->addProcess((new HotReload('HotReload', ['disableInotify' => true]))->getProcess());
    }

    /**
     * @throws \EasySwoole\Pool\Exception\Exception
     * 注册mysql连接对象
     */
    private static function mysql_pool_conn()
    {
        $mysqlConf = Config::getInstance()->getConf('MYSQL');
        try {
            $config = new \EasySwoole\ORM\Db\Config();
            // 基础配置
            $config->setDatabase($mysqlConf['database']);
            $config->setUser($mysqlConf['user']);
            $config->setPassword($mysqlConf['password']);
            $config->setHost($mysqlConf['host']);

            //连接池配置
            $config->setGetObjectTimeout(3.0); //设置获取连接池对象超时时间
            $config->setIntervalCheckTime(30 * 1000); //设置检测连接存活执行回收和创建的周期
            $config->setMaxIdleTime(15); //连接池对象最大闲置时间(秒)
            $config->setMaxObjectNum(20); //设置最大连接池存在连接对象数量
            $config->setMinObjectNum(5); //设置最小连接池存在连接对象数量
            //创建连接
            DbManager::getInstance()->addConnection(new Connection($config));
            DbManager::getInstance()->addConnection(new Connection($config), 'liuqiang');
        } catch (\EasySwoole\ORM\Exception\Exception $e) {
            dd('注册mysql服务失败:' . $e->getMessage(), 'register_pool');
        }
        return true;
    }

    // 注册通用链接池 自定义redis连接池
    private static function redis_pool_conn()
    {
        $poolConf = new \EasySwoole\Pool\Config();
        //获取redis配置文件 不想使用自带的配置文件 可以传入自定义配置文件
//        Config::getInstance()->getConf('REDIS'); //获取自定义配置文件
        $redisConf = new RedisConfig();
        Manager::getInstance()->register(new RedisPool($poolConf, $redisConf), 'redis_pool');
    }

    //官方redis_pool
    private static function redis_pool()
    {
        \EasySwoole\RedisPool\Redis::getInstance()->register('redis', new RedisConfig());
    }


    /**
     * @param Request $request
     * @param Response $response
     * @return bool
     * 设置跨域问题
     */
    private static function SetCrossDomain(Request $request, Response $response)
    {
//        $origin = $request->getHeader('origin');
//        if ($origin != []) {
//            $response->withHeader('Access-Control-Allow-Origin', '*');
//            $response->withHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
//            $response->withHeader('Access-Control-Allow-Credentials', 'true');
//            $response->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, token');
//            if ($request->getMethod() == 'OPTIONS') {
//                $response->withStatus(Status::CODE_OK);
//                return false;
//            }
//        }
//        $response->withHeader('Content-type', 'application/json;charset=utf-8');
        return true;
    }

    //记录请求日志
    private static function setRequestLog(Request $request)
    {
        go(function () use ($request) {
            $ip = $request->getHeaders()['x-real-ip'];
            $data = $request->getRequestParam();
            $data['ip'] = array_shift($ip);
            $data['time'] = time();
            $data['path'] = $request->getUri()->getPath();
            if (!empty($data)) {
                $path = __DIR__ . '/Log/request/' . date('Y-m') . '/' . date('Y-m-d') . '.log';
                if (!is_dir(dirname($path))) {
                    mkdir(dirname($path), 0777, true);
                }
                file_put_contents($path, json_encode($data) . PHP_EOL, FILE_APPEND);
            }
        });
        return true;
    }

    //注册消费队列进程 及时类型 马上投递马上消费这种
    private static function registerQueueProcess()
    {
        //redis pool使用请看redis 章节文档
        $config = new RedisConfig();  //获取redis默认配置文件
        $redis = new RedisPoolEasy($config); //注册redis连接池
        $driver = new Redis($redis, 'test_queue'); //注册队列
        TestQueue::getInstance($driver); //注册到队列中
        //注册一个消费进程
        ServerManager::getInstance()->addProcess(new TestQueueProcess(), 'test_queue_process'); //消费队列注册到进程管理中
    }


    /**
     * @throws \EasySwoole\Component\Process\Exception
     * @throws \EasySwoole\FastCache\Exception\RuntimeError
     * 注册消费队列服务进程
     * 虚拟机无法测试
     */
    private static function registerFastCacheQueueServer()
    {
        Cache::getInstance()->setTempDir(EASYSWOOLE_TEMP_DIR)->attachToServer(ServerManager::getInstance()->getSwooleServer());
    }

    /**
     * 注册消费队列进程 延时类型 投递时候设置延时 时间到了在执行
     * 虚拟机无法测试
     */
    private static function registerQueueProcessConsumeProcess()
    {
        $processConfig = new \EasySwoole\Component\Process\Config();
        $processConfig->setProcessName('QueueProcessConsumeProcess');
        /*
         * 传递给进程的参数
        */
//        $processConfig->setArg([
//            'arg1'=>time()
//        ]);
        ServerManager::getInstance()->getSwooleServer()->addProcess((new FastCacheQueue($processConfig))->getProcess());

    }

    /**
     * 启动时候检查日志目录 没有就创建
     */
    private static function is_log_dir()
    {
        $dir = [__DIR__ . '/Log/request/', __DIR__ . '/Log/process/'];
        foreach ($dir as $v) {
            if (!is_dir($v)) {
                mkdir($v, 0777, true);
            }
        }
    }

    /**
     * @param EventRegister $register
     * @throws \EasySwoole\Socket\Exception\Exception
     * 注册 websocket 服务
     */
    private static function registerWebSocketServer(EventRegister $register)
    {
        // 创建一个 Dispatcher 配置
        $conf = new \EasySwoole\Socket\Config();
        // 设置 Dispatcher 为 WebSocket 模式
        $conf->setType(\EasySwoole\Socket\Config::WEB_SOCKET);
        // 设置解析器对象
        $conf->setParser(new WebSocketParser());
        // 设置解析器 解析错误异常处理
        $conf->setOnExceptionHandler(function (\swoole_websocket_server $server, \Throwable $throwable, $data, WebSocket $client, \EasySwoole\Socket\Bean\Response $response) {
            $server->push($client->getFd(), JsonSendMessage::getInstance()->errorJson($throwable->getMessage()));
            $server->close($client->getFd());
        });

        // 创建 Dispatcher 对象 并注入 config 对象
        $dispatch = new Dispatcher($conf);

        $register->set(EventRegister::onOpen, function (\swoole_websocket_server $server, \swoole_http_request $request) {
//            if ($request->get['token'] != 98765432) {
//                $server->push($request->fd, '错误');
//                $server->close($request->fd);
//            }
            //todo 1获取数据 验证用户等
            //todo 2验证成功 查询数据
            $user = UserModel::getInstance()->where(['id' => 1])->findOne();
            // todo 3 用户状态进行 绑定uid 业务数据请求服务端唯一标识
            $server->bind($request->fd, $user['id']);
        });
        // 给server 注册相关事件 在 WebSocket 模式下  on message 事件必须注册 并且交给 Dispatcher 对象处理
        $register->set(EventRegister::onMessage, function (\swoole_websocket_server $server, \swoole_websocket_frame $frame) use ($dispatch) {
//            var_dump($server->getClientInfo($frame->fd));
            $dispatch->dispatch($server, $frame->data, $frame);
        });
        // 注册关闭事件
        $register->set(EventRegister::onClose, function (\swoole_websocket_server $server, $fd) {
//            echo $fd . '请求已关闭', PHP_EOL;
            $server->close($fd);
            // todo 断开连接清除一些数据等逻辑操作
        });
    }


    /**
     * 注册tcp服务端
     *
     */
    private static function registerTcpServer()
    {
        //主服务管理器
        $server = ServerManager::getInstance()->getSwooleServer();
        //添加监听子进程端口监听
        $subPort1 = $server->addlistener('0.0.0.0', 9502, SWOOLE_TCP);
        $config = Config::getInstance()->getConf('TCP_DATA');
        if ($config['open_length_check']) {
            //是否验证数据包  true 验证 false 不验证
            $subPort1->set($config['set_data']);
        } else {
            $subPort1->set(['open_length_check' => false]);
        }

        // 创建一个 Dispatcher 配置
        $conf = new \EasySwoole\Socket\Config();
        // 设置 Dispatcher 为 WebSocket 模式
        $conf->setType(\EasySwoole\Socket\Config::TCP);
        // 设置解析器对象
        $conf->setParser(new TcpParser());
        // 设置解析器 解析错误异常处理
        $conf->setOnExceptionHandler(function (\swoole_websocket_server $server, \Throwable $throwable, $data, $client, $response) {
            $server->send($client->getFd(), JsonSendMessage::getInstance()->errorJson($throwable->getMessage()));
            $server->close($client->getFd());
        });

        // 创建 Dispatcher 对象 并注入 config 对象
        $dispatch = new Dispatcher($conf);

        //链接成功回调
        $subPort1->on(EventRegister::onConnect, function (\swoole_server $server, int $fd, int $reactor_id) {
            $str = '恭喜你连接成功';
            $server->send($fd, $str);
        });
        // 关闭事件
        $subPort1->on(EventRegister::onClose, function (\swoole_server $server, int $fd, int $reactor_id) {

        });
        // 接收到客户端消息
        $subPort1->on(EventRegister::onReceive, function (\swoole_server $server, int $fd, int $reactor_id, string $data) use ($dispatch) {
            // 消息处理交给 解析器
            $dispatch->dispatch($server, $data, $fd, $reactor_id);
        });
    }


    // 注册通用链接池 自定义 rabbitmq 连接池
    private static function rabbitmq_pool_conn()
    {
        $poolConf = new \EasySwoole\Pool\Config();
        //获取redis配置文件 不想使用自带的配置文件 可以传入自定义配置文件
        $rabbConfig = Config::getInstance()->getConf('RABBITMQ'); //获取自定义配置文件
        Manager::getInstance()->register(new RabbitmqPool($poolConf, $rabbConfig), $rabbConfig['pool_name']);
    }

}
